﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Reflection;
using System.Threading;
using Zero.Common;

namespace Zero.Backend
{
    /// <summary>
    /// Processes request through <see cref="IBackend"/> and <see cref="ITypedBackend"/>.
    /// </summary>
    /// <remarks>
    /// Uses reflection throught list of all available object, that implement <see cref="IRequestHandler"/> to find appropriate method to handle specific request.
    /// </remarks>
    [Export(typeof(IBackend)), Export(typeof(ITypedBackend))]
    public class Backend : IBackend, ITypedBackend
    {
        /// <summary>
        /// Inicializes this backend with specific handlers. Used by MEF.
        /// </summary>
        /// <param name="handlers"></param>
        [ImportingConstructor]
        public Backend([ImportMany]IEnumerable<IRequestHandler> handlers)
        {
            _handlers.AddRange(handlers);
        }

        readonly IList<IRequestHandler> _handlers = new List<IRequestHandler>();

        /// <summary>
        /// Implementation of <see cref="IBackend.Process"/>.
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public object Process(object request)
        {
            foreach (IRequestHandler handler in _handlers)
            {
                var methods = handler.GetType().GetMethods();
                var method = methods.FirstOrDefault(x => IsValidMethod(x, request.GetType()));

                if (method != null)
                {
                    return method.Invoke(handler, new object[] { request });
                }
            }

            throw new ApplicationException("Cannot find suitable handler to process " + request.GetType() + "."  );
        }

        /// <summary>
        /// Implementation of <see cref="ITypedBackend.Process{TResponse}"/>
        /// </summary>
        /// <typeparam name="TResponse"></typeparam>
        /// <param name="request"></param>
        /// <returns></returns>
        public TResponse Process<TResponse>(IRequest<TResponse> request)
        {
            return (TResponse)Process((object)request);
        }

        private bool IsValidMethod(MethodInfo method, Type parameterType)
        {
            var parameters = method.GetParameters();
            if (parameters.Length != 1) return false;

            if (parameters[0].ParameterType == parameterType) return true;
            else return false;
        }
    }
}
